/*********************************************************************************
 *      Copyright:  (C) 2022 zengwen<1825984037@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  server.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(03/05/22)
 *         Author:  zengwen <1825984037@qq.com>
 *      ChangeLog:  1, Release initial version on "03/05/22 13:07:26"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/resource.h>
#include <signal.h>
#include "sqlite3.h"
#include "logger.h"
#include "database_server.h"
#include "socket_server_init.h"

#define  MAX_EVENTS     512
#define  ARRAY_SIZE(x)  (sizeof(x)/sizeof(x[0]))//求数组中元素的个数

void set_socket_rlimit(void);
void print_usage(char *progname);
void sig_handle(int signum);



int main (int argc, char **argv)
{
	char                *progname=NULL;
	int                  ch;
	int                  deamon_run=0;
	int                  serv_port=0;
	int                  ret=-1;
	int                  listenfd;
	int                  connfd;
	int                  epollfd;
	struct epoll_event   event;//用来添加fd
	struct epoll_event   event_array[MAX_EVENTS];//同时处理多少个客户端,有关就绪态fd的信息
	int                  events;//多少个客户端发生的事件
	int                  rv,i,j;
	char                 buf[1024];
	int                  found;
	sqlite3             *db=NULL;

	struct option        opts[]=
	{
		{"deamon",no_argument,NULL,'b'},
		{"port",required_argument,NULL,'p'},
		{"help",no_argument,NULL,'h'},
		{NULL,0,NULL,0}
	};
	progname = basename(argv[0]);
	while((ch=getopt_long(argc,argv,"bp:h",opts,NULL))!=-1)
	{
		switch(ch)
		{
			case 'b':
				deamon_run=1;
				break;
			case 'p':
				serv_port=atoi(optarg);
				break;
			case 'h':
				print_usage(argv[0]);
				return 0;
			default:
				break;
		}
	}
	if (!serv_port)
	{
		print_usage(progname);
		return 0;
	}

	set_socket_rlimit();
	if(deamon_run)//守护进程
	{
		daemon(0,0);
	}

/*	signal(SIGTERM,sig_handle);
	signal(SIGALRM,sig_handle);
	signal(SIGINT,sig_handle);*/

	ret = logger_init("/home/zengwen/server.log",LOG_LEVEL_MAX,1024);
	if(ret < 0)
	{
		fprintf(stderr,"initial logger system failure\n");
		return -2;
	}
	log_info("Logger system initialise ok\n");

	if((listenfd=socket_sever_init(NULL,serv_port))<0)
	{
		log_err("ERROR:%s server listen on port %d failure\n",argv[0],serv_port);
		return -3;
	}
	printf("%s server start listen on port %d\n",argv[0],serv_port);

	if(open_data_sqlite(&db))
	{
		printf("open database failure:%s\n",strerror(errno));
		return -4;
		goto cleanup;
	}
	printf("open the database sucessfuly!\n");

	if((epollfd=epoll_create(MAX_EVENTS))<0)
	{
		printf("Create epoll failure:%s\n",strerror(errno));
		return -5;
		goto cleanup;
	}
	log_info("Create epoll sucessfuly!\n");

	event.events=EPOLLIN;//把listenfd设置为非阻塞可读
	event.data.fd=listenfd;
	if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event)<0)//把listenfd添加到epollfd所代表的epoll兴趣列表
	{
		printf("epoll add listen socket failure:%s\n",strerror(errno));
		return -6;
		goto cleanup;
	}
	log_info("epoll add listen %d socket sucessfuly!\n",listenfd);

	for( ; ; )
	{
		//进程会被阻塞在这
		events=epoll_wait(epollfd,event_array,MAX_EVENTS,-1);
		if(events<0)//返回值<0，说明epoll_wait出错了
		{
			printf("epoll failure:%s\n",strerror(errno));
			break;
		}
		else if(events==0)//返回值=0说明超时
		{
			log_info("epoll get time out\n");
			continue;
		}
		for(i=0; i<events; i++)//event>0,代表发生事件的数目
		{
			if((event_array[i].events&EPOLLERR)||(event_array[i].events&EPOLLHUP))//有错误发生或发生挂断
			{
				printf("epoll_wait get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno));

				epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);//从兴趣列表移除此fd
				close(event_array[i].data.fd);
			}
			if( event_array[i].data.fd == listenfd )//有新的连接
			{
				if( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0)
				{
					printf("accept new client failure: %s\n", strerror(errno));
					continue;
				}
				//accept成功
				event.data.fd = connfd;//把新的客户端fd添加到event
				event.events = EPOLLIN|EPOLLET;
				if( epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event) < 0 )//把connfd加到epollfd中
				{
					printf("epoll add client socket failure: %s\n", strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				printf("epoll add new client socket[%d] ok.\n", connfd);
			}
			else//已经连接上客户端
			{
				memset(buf,0,sizeof(buf));
				if( (rv=read(event_array[i].data.fd, buf, sizeof(buf))) <= 0)//read失败
				{
					printf("socket[%d] read failure or get disconncet and will be removed:%s.\n",event_array[i].data.fd,strerror(errno));
					epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
					close(event_array[i].data.fd);
					continue;
				}

				else//read成功
				{
					printf("socket[%d] read get %d bytes data\n", event_array[i].data.fd, rv);
					printf("Server has received massege:%s\n",buf);
					save_data_sqlite(db, buf);//保存数据到数据库

				/*	if( write(event_array[i].data.fd, buf, rv) < 0 )
					{
						printf("socket[%d] write failure: %s\n", event_array[i].data.fd, strerror(errno));
						epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
						close(event_array[i].data.fd);
					}*/
				}
			}
		}
	}

cleanup:
	close(listenfd);
	sqlite3_close(db);
	logger_term();
	return 0;
}


void set_socket_rlimit(void)//把当前一个进程可打开的最大fd数目设为最大
{
	struct rlimit limit={0};

	getrlimit(RLIMIT_NOFILE,&limit);
	limit.rlim_cur=limit.rlim_max;
	setrlimit(RLIMIT_NOFILE,&limit);

	printf ("set socket open fd max count to %d \n",limit.rlim_max);
}

//参数解析
void print_usage(char *progname)
{
	printf ("usage:%s [OPTION]...\n",progname);
	printf ("-b(--deamon):set program runing on background\n");
	printf ("-p(--port):socket server port address\n");
	printf ("-h(--help):print help information\n");
	printf ("\nExample:%s -b -p 3333\n",progname);
	return ;
}

//安装信号
void sig_handle(int signum)
{
	if( SIGTERM == signum)
	{
		log_warn("the program will be exit.\n");
		exit(0);
	}
}

